%% -*- coding: utf-8 -*-

%% @doc JSONタームをtemplerlコンテキストに変換
-module(templ_json).
% -compile(export_all).
-export([convert_to_context/1,convert_to_erlang/1,convert/2]).

%% オプション名、値はアトム
-define(NULL_TO_WHAT, null).
-define(OBJ_HANDLER, obj_handler).
-define(PAIR_HANDLER, pair_handler).
-define(ARRAY_HANDLER, array_handler).
-define(NUM_HANDLER, num_handler).
-define(STR_HANDLER, str_handler).

-define(binary_to_atom(B), list_to_atom(binary_to_list(B))).

%% DEFAULTの値に特に意味はない。アトムでなければ何でもよい。
-define(DEFAULT, []). 

%% @type json_value() = serialize:json_value() .

convert_to_context(JsonData) ->
	convert(JsonData, 
			[{?NULL_TO_WHAT,  none},
			 {?OBJ_HANDLER,   fun(Proplist) -> Proplist end},
			 {?PAIR_HANDLER,  fun(Key, Val) -> {Key, Val} end},
			 {?ARRAY_HANDLER, fun(Head, Tail) -> [Head|Tail] end},
			 {?NUM_HANDLER,   fun(Number) -> Number end},
			 {?STR_HANDLER,   fun(String) -> String end}]).

convert_to_erlang(JsonData) ->
	convert(JsonData, 
			[{?NULL_TO_WHAT,  none},
			 {?OBJ_HANDLER,   fun(Proplist) -> {obj,Proplist} end},
			 {?PAIR_HANDLER,  fun(Key, Val) -> {Key, Val} end},
			 {?ARRAY_HANDLER, fun(Head, Tail) -> [Head|Tail] end},
			 {?NUM_HANDLER,   fun(Number) -> Number end},
			 {?STR_HANDLER,   fun(String) -> String end}]).


%% @spec (JsonData::json_value(), Opts::options()) -> term()
%% @doc JSONデータを通常のErlang形式に変関する.
%% 変換の指定は第二引数のオプションで行う。
%% @end

%% 特殊なアトム値 true/false/null
convert(true, _Opts) ->
	true;
convert(false, _Opts) ->
	false;
convert(null, Opts) ->
	case proplists:get_value(?NULL_TO_WHAT, Opts, ?DEFAULT) of
		Null when is_atom(Null) ->
			Null;
		?DEFAULT ->
			null
	end;
%% 数値
convert(Number, Opts) when is_number(Number) ->
	case proplists:get_value(?NUM_HANDLER, Opts, ?DEFAULT) of
		Handler when is_function(Handler) ->
			Handler(Number);
		?DEFAULT ->
			Number
	end;
%% 文字列
convert(Bin, Opts) when is_binary(Bin) ->
	case proplists:get_value(?STR_HANDLER, Opts, ?DEFAULT) of
		Handler when is_function(Handler) ->
			Str = binary_to_list(Bin),
			Handler(Str);
		?DEFAULT ->
			Bin
	end;

%% 配列
convert([], _Opts) ->
	[];
convert([Head|Tail], Opts) ->
	case proplists:get_value(?ARRAY_HANDLER, Opts, ?DEFAULT) of
		Handler when is_function(Handler) ->
			Handler(convert(Head, Opts), convert(Tail, Opts));
		?DEFAULT ->
			[convert(Head, Opts) | convert(Tail, Opts)]
	end;

%% オブジェクト
convert({obj, Proplist}, Opts) ->
	Proplist2 = case proplists:get_value(?PAIR_HANDLER, Opts, ?DEFAULT) of
					PHandler when is_function(PHandler) ->
						lists:map(fun(Prop) -> convert_value(Prop, PHandler, Opts) end, 
								  Proplist);
					?DEFAULT ->
						lists:map(fun(Prop) -> convert_value(Prop, Opts) end, 
								  Proplist)
				end,
	case proplists:get_value(?OBJ_HANDLER, Opts, ?DEFAULT) of
		OHandler when is_function(OHandler) ->
			OHandler(Proplist2);
		?DEFAULT ->
			Proplist2
	end.

convert_value({Key, Val}, Handler, Opts) when is_binary(Key) ->  
	AtomKey = ?binary_to_atom(Key),
	Handler(AtomKey, convert(Val, Opts));
convert_value({Key, Val}, Handler, Opts) ->  
	Handler(Key, convert(Val, Opts)).
convert_value({Key, Val}, Opts) ->  
	{Key, convert(Val, Opts)}.
